"use client"

import {
   Check,
   ChevronRight,
   Clipboard,
   File,
   Folder,
   Fullscreen,
   Monitor,
   Smartphone,
   Tablet,
   Terminal,
} from "lucide-react"
import Image from "next/image"
import Link from "next/link"
import type { registryItemFileSchema, registryItemSchema } from "nyxb/registry"
import * as React from "react"
import type { ImperativePanelHandle } from "react-resizable-panels"
import type { z } from "zod"

import { V0Button } from "~/components/v0-button"
import { useCopyToClipboard } from "~/hooks/use-copy-to-clipboard"
import { trackEvent } from "~/lib/events"
import type {
   FileTree,
   createFileTreeForRegistryItemFiles,
} from "~/lib/registry"
import { Button } from "~/registry/miami/ui/button"
import {
   Collapsible,
   CollapsibleContent,
   CollapsibleTrigger,
} from "~/registry/miami/ui/collapsible"
import {
   ResizableHandle,
   ResizablePanel,
   ResizablePanelGroup,
} from "~/registry/miami/ui/resizable"
import { Separator } from "~/registry/miami/ui/separator"
import {
   Sidebar,
   SidebarGroup,
   SidebarGroupContent,
   SidebarGroupLabel,
   SidebarMenu,
   SidebarMenuButton,
   SidebarMenuItem,
   SidebarMenuSub,
   SidebarProvider,
} from "~/registry/miami/ui/sidebar"
import { Tabs, TabsList, TabsTrigger } from "~/registry/miami/ui/tabs"
import { ToggleGroup, ToggleGroupItem } from "~/registry/miami/ui/toggle-group"
import type { Style } from "~/registry/registry-styles"

type BlockViewerContext = {
   item: z.infer<typeof registryItemSchema>
   view: "code" | "preview"
   setView: (view: "code" | "preview") => void
   style?: Style["name"]
   setStyle: (style: Style["name"]) => void
   activeFile: string | null
   setActiveFile: (file: string) => void
   resizablePanelRef: React.RefObject<ImperativePanelHandle> | null
   tree: ReturnType<typeof createFileTreeForRegistryItemFiles> | null
   highlightedFiles:
      | (z.infer<typeof registryItemFileSchema> & {
           highlightedContent: string
        })[]
      | null
}

const BlockViewerContext = React.createContext<BlockViewerContext | null>(null)

function useBlockViewer() {
   const context = React.useContext(BlockViewerContext)
   if (!context) {
      throw new Error(
         "useBlockViewer must be used within a BlockViewerProvider.",
      )
   }
   return context
}

function BlockViewerProvider({
   item,
   tree,
   highlightedFiles,
   children,
}: Pick<BlockViewerContext, "item" | "tree" | "highlightedFiles"> & {
   children: React.ReactNode
}) {
   const [view, setView] = React.useState<BlockViewerContext["view"]>("preview")
   const [style, setStyle] =
      React.useState<BlockViewerContext["style"]>("miami")
   const [activeFile, setActiveFile] = React.useState<
      BlockViewerContext["activeFile"]
   >(highlightedFiles?.[0].target ?? null)
   const resizablePanelRef = React.useRef<ImperativePanelHandle>(null)

   return (
      <BlockViewerContext.Provider
         value={{
            item,
            view,
            setView,
            style,
            setStyle,
            resizablePanelRef,
            activeFile,
            setActiveFile,
            tree,
            highlightedFiles,
         }}
      >
         <div
            id={item.name}
            data-view={view}
            className="group/block-view-wrapper flex min-w-0 flex-col items-stretch gap-4"
            style={
               {
                  "--height": item.meta?.iframeHeight ?? "930px",
               } as React.CSSProperties
            }
         >
            {children}
         </div>
      </BlockViewerContext.Provider>
   )
}

function BlockViewerToolbar() {
   const { setView, item, resizablePanelRef, style } = useBlockViewer()
   const { copyToClipboard, isCopied } = useCopyToClipboard()

   return (
      <div className="flex w-full items-center gap-2 md:pr-[14px]">
         <Tabs
            defaultValue="preview"
            onValueChange={(value) => setView(value as "preview" | "code")}
            className="hidden lg:flex"
         >
            <TabsList className="h-7 items-center rounded-md p-0 px-[calc(theme(spacing.1)_-_2px)] py-[theme(spacing.1)]">
               <TabsTrigger
                  value="preview"
                  className="h-[1.45rem] rounded-sm px-2 text-xs"
               >
                  Preview
               </TabsTrigger>
               <TabsTrigger
                  value="code"
                  className="h-[1.45rem] rounded-sm px-2 text-xs"
               >
                  Code
               </TabsTrigger>
            </TabsList>
         </Tabs>
         <Separator
            orientation="vertical"
            className="mx-2 hidden h-4 lg:flex"
         />
         <a
            href={`#${item.name}`}
            className="text-sm font-medium underline-offset-2 hover:underline"
         >
            {item.description}
         </a>
         <div className="ml-auto hidden items-center gap-2 md:flex">
            <div className="hidden h-7 items-center gap-1.5 rounded-md border p-[2px] shadow-none lg:flex">
               <ToggleGroup
                  type="single"
                  defaultValue="100"
                  onValueChange={(value) => {
                     if (resizablePanelRef?.current) {
                        resizablePanelRef.current.resize(Number.parseInt(value))
                     }
                  }}
               >
                  <ToggleGroupItem
                     value="100"
                     className="h-[22px] w-[22px] min-w-0 rounded-sm p-0"
                     title="Desktop"
                  >
                     <Monitor className="h-3.5 w-3.5" />
                  </ToggleGroupItem>
                  <ToggleGroupItem
                     value="60"
                     className="h-[22px] w-[22px] min-w-0 rounded-sm p-0"
                     title="Tablet"
                  >
                     <Tablet className="h-3.5 w-3.5" />
                  </ToggleGroupItem>
                  <ToggleGroupItem
                     value="30"
                     className="h-[22px] w-[22px] min-w-0 rounded-sm p-0"
                     title="Mobile"
                  >
                     <Smartphone className="h-3.5 w-3.5" />
                  </ToggleGroupItem>
                  <Separator orientation="vertical" className="h-4" />
                  <Button
                     size="icon"
                     variant="ghost"
                     className="h-[22px] w-[22px] rounded-sm p-0"
                     asChild
                     title="Open in New Tab"
                  >
                     <Link
                        href={`/view/styles/${style}/${item.name}`}
                        target="_blank"
                     >
                        <span className="sr-only">Open in New Tab</span>
                        <Fullscreen className="h-3.5 w-3.5" />
                     </Link>
                  </Button>
               </ToggleGroup>
            </div>
            <Separator
               orientation="vertical"
               className="mx-1 hidden h-4 md:flex"
            />
            <div className="flex h-7 items-center gap-1 rounded-md border p-[2px]">
               <Button
                  variant="ghost"
                  className="hidden h-[22px] w-auto gap-1 rounded-sm px-2 md:flex lg:w-auto"
                  size="sm"
                  onClick={() => {
                     copyToClipboard(`npx nyxb@latest add ${item.name}`)
                  }}
               >
                  {isCopied ? <Check /> : <Terminal />}
                  <span className="hidden lg:inline">
                     npx nyxb add {item.name}
                  </span>
               </Button>
            </div>
            <Separator
               orientation="vertical"
               className="mx-1 hidden h-4 xl:flex"
            />
            <V0Button
               className="hidden shadow-none sm:flex"
               id={`v0-button-${item.name}`}
               name={`${item.name}`}
            />
         </div>
      </div>
   )
}

function BlockViewerView() {
   const { item, style, resizablePanelRef } = useBlockViewer()

   return (
      <div className="group-data-[view=code]/block-view-wrapper:hidden md:h-[--height]">
         <div className="grid w-full gap-4">
            <ResizablePanelGroup
               direction="horizontal"
               className="relative z-10"
            >
               <ResizablePanel
                  ref={resizablePanelRef}
                  className="relative aspect-[4/2.5] rounded-xl border bg-background md:aspect-auto"
                  defaultSize={100}
                  minSize={30}
               >
                  <Image
                     src={`/r/styles/${style}/${item.name}-light.png`}
                     alt={item.name}
                     data-block={item.name}
                     width={1440}
                     height={900}
                     className="object-cover dark:hidden md:hidden md:dark:hidden"
                  />
                  <Image
                     src={`/r/styles/${style}/${item.name}-dark.png`}
                     alt={item.name}
                     data-block={item.name}
                     width={1440}
                     height={900}
                     className="hidden object-cover dark:block md:hidden md:dark:hidden"
                  />
                  <iframe
                     src={`/view/styles/${style}/${item.name}`}
                     height={item.meta?.iframeHeight ?? 930}
                     className="relative z-20 hidden w-full bg-background md:block"
                  />
               </ResizablePanel>
               <ResizableHandle className="relative hidden w-3 bg-transparent p-0 after:absolute after:right-0 after:top-1/2 after:h-8 after:w-[6px] after:-translate-y-1/2 after:translate-x-[-1px] after:rounded-full after:bg-border after:transition-all after:hover:h-10 md:block" />
               <ResizablePanel defaultSize={0} minSize={0} />
            </ResizablePanelGroup>
         </div>
      </div>
   )
}

function BlockViewerCode() {
   const { activeFile, highlightedFiles } = useBlockViewer()

   const file = React.useMemo(() => {
      return highlightedFiles?.find((file) => file.target === activeFile)
   }, [highlightedFiles, activeFile])

   if (!file) {
      return null
   }

   return (
      <div className="flex overflow-hidden rounded-xl bg-zinc-950 text-white group-data-[view=preview]/block-view-wrapper:hidden md:h-[--height]">
         <div className="w-[280px]">
            <BlockViewerFileTree />
         </div>
         <div className="flex min-w-0 flex-1 flex-col">
            <div className="flex h-12 items-center gap-2 border-b border-zinc-700 bg-zinc-900 px-4 text-sm font-medium">
               <File className="size-4" />
               {file.target}
               <div className="ml-auto flex items-center gap-2">
                  <BlockCopyCodeButton />
               </div>
            </div>
            <div
               key={file?.path}
               data-rehype-pretty-code-fragment
               dangerouslySetInnerHTML={{
                  __html: file?.highlightedContent ?? "",
               }}
               className="relative flex-1 overflow-hidden [&_.line:before]:sticky [&_.line:before]:left-2 [&_.line:before]:z-10 [&_.line:before]:translate-y-[-1px] [&_.line:before]:pr-1 [&_pre]:h-[--height] [&_pre]:overflow-auto [&_pre]:!bg-transparent [&_pre]:pb-20 [&_pre]:pt-4 [&_pre]:pl-6 [&_pre]:font-mono [&_pre]:text-sm [&_pre]:leading-relaxed"
            />
         </div>
      </div>
   )
}

export function BlockViewerFileTree() {
   const { tree } = useBlockViewer()

   if (!tree) {
      return null
   }

   return (
      <SidebarProvider className="flex !min-h-full flex-col">
         <Sidebar
            collapsible="none"
            className="w-full flex-1 border-r border-zinc-700 bg-zinc-900 text-white"
         >
            <SidebarGroupLabel className="h-12 rounded-none border-b border-zinc-700 px-4 text-sm text-white">
               Files
            </SidebarGroupLabel>
            <SidebarGroup className="p-0">
               <SidebarGroupContent>
                  <SidebarMenu className="gap-1.5">
                     {tree.map((file, index) => (
                        <Tree key={index} item={file} index={1} />
                     ))}
                  </SidebarMenu>
               </SidebarGroupContent>
            </SidebarGroup>
         </Sidebar>
      </SidebarProvider>
   )
}

function Tree({ item, index }: { item: FileTree; index: number }) {
   const { activeFile, setActiveFile } = useBlockViewer()

   if (!item.children) {
      return (
         <SidebarMenuItem>
            <SidebarMenuButton
               isActive={item.path === activeFile}
               onClick={() => item.path && setActiveFile(item.path)}
               className="whitespace-nowrap rounded-none pl-[--index] hover:bg-zinc-700 hover:text-white focus:bg-zinc-700 focus:text-white focus-visible:bg-zinc-700 focus-visible:text-white active:bg-zinc-700 active:text-white data-[active=true]:bg-zinc-700 data-[active=true]:text-white"
               data-index={index}
               style={
                  {
                     "--index": `${index * (index === 2 ? 1.2 : 1.3)}rem`,
                  } as React.CSSProperties
               }
            >
               <ChevronRight className="invisible" />
               <File className="h-4 w-4" />
               {item.name}
            </SidebarMenuButton>
         </SidebarMenuItem>
      )
   }

   return (
      <SidebarMenuItem>
         <Collapsible
            className="group/collapsible [&[data-state=open]>button>svg:first-child]:rotate-90"
            defaultOpen
         >
            <CollapsibleTrigger asChild>
               <SidebarMenuButton
                  className="whitespace-nowrap rounded-none pl-[--index] hover:bg-zinc-700 hover:text-white focus-visible:bg-zinc-700 focus-visible:text-white active:bg-zinc-700 active:text-white data-[active=true]:bg-zinc-700 data-[active=true]:text-white data-[state=open]:hover:bg-zinc-700 data-[state=open]:hover:text-white"
                  style={
                     {
                        "--index": `${index * (index === 1 ? 1 : 1.2)}rem`,
                     } as React.CSSProperties
                  }
               >
                  <ChevronRight className="h-4 w-4 transition-transform" />
                  <Folder className="h-4 w-4" />
                  {item.name}
               </SidebarMenuButton>
            </CollapsibleTrigger>
            <CollapsibleContent>
               <SidebarMenuSub className="m-0 w-full border-none p-0">
                  {item.children.map((subItem, key) => (
                     <Tree key={key} item={subItem} index={index + 1} />
                  ))}
               </SidebarMenuSub>
            </CollapsibleContent>
         </Collapsible>
      </SidebarMenuItem>
   )
}

function BlockCopyCodeButton() {
   const { activeFile, item } = useBlockViewer()
   const { copyToClipboard, isCopied } = useCopyToClipboard()

   const file = React.useMemo(() => {
      return item.files?.find((file) => file.target === activeFile)
   }, [activeFile, item.files])

   const content = file?.content

   if (!content) {
      return null
   }

   return (
      <Button
         onClick={() => {
            copyToClipboard(content)
            trackEvent({
               name: "copy_block_code",
               properties: {
                  name: item.name,
                  file: file.path,
               },
            })
         }}
         className="h-7 w-7 shrink-0 rounded-lg p-0 hover:bg-zinc-700 hover:text-white focus:bg-zinc-700 focus:text-white focus-visible:bg-zinc-700 focus-visible:text-white active:bg-zinc-700 active:text-white data-[active=true]:bg-zinc-700 data-[active=true]:text-white [&>svg]:size-3"
         variant="ghost"
      >
         {isCopied ? <Check /> : <Clipboard />}
      </Button>
   )
}

function BlockViewer({
   item,
   tree,
   highlightedFiles,
   ...props
}: Pick<BlockViewerContext, "item" | "tree" | "highlightedFiles">) {
   return (
      <BlockViewerProvider
         item={item}
         tree={tree}
         highlightedFiles={highlightedFiles}
         {...props}
      >
         <BlockViewerToolbar />
         <BlockViewerView />
         <BlockViewerCode />
      </BlockViewerProvider>
   )
}

export { BlockViewer }
